package org.ntu.csn.doraemon.canvas;

import java.io.IOException;
import java.io.InputStream;
import java.util.Random;
import java.util.Timer;

import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;
import javax.microedition.lcdui.game.GameCanvas;
import javax.microedition.lcdui.game.LayerManager;
import javax.microedition.lcdui.game.Sprite;
import javax.microedition.lcdui.game.TiledLayer;
import javax.microedition.media.Manager;
import javax.microedition.media.Player;

import org.ntu.csn.doraemon.model.Mark;
import org.ntu.csn.doraemon.sprite.Bomb2s;
import org.ntu.csn.doraemon.sprite.Bombs;
import org.ntu.csn.doraemon.sprite.Clock;
import org.ntu.csn.doraemon.sprite.DoraemonSprite;
import org.ntu.csn.doraemon.sprite.Dorayakis;
import org.ntu.csn.doraemon.sprite.Enhancers;
import org.ntu.csn.doraemon.sprite.FairyWaters;
import org.ntu.csn.doraemon.sprite.Lifes;
import org.ntu.csn.doraemon.sprite.Poisons;
import org.ntu.csn.doraemon.tool.Constant;
import org.ntu.csn.doraemon.tool.GameTools;

public class Main extends GameCanvas implements Runnable {



	public Main(StartMenu sm) {
		super(true);
		this.sm = sm;
	}

	public void start(int maxDorayaki, int maxBomb, int maxBomb2, int maxFairyWater,
			int maxEnhancer,int maxPoison, int maxLife) {

		try {
			this.maxBomb2s = maxBomb2;
			this.maxBombs = maxBomb;
			this.maxDoraykis = maxDorayaki;
			this.maxEnhancers = maxEnhancer;
			this.maxFairyWates = maxFairyWater;
			this.maxPoisons = maxPoison;
			this.maxLifes = maxLife;
			this.currentLife = maxLife;
			
			// creates the layermanager
			manager = new LayerManager();
			// creates the characters
			createSprites();
			
			// creates the game background
			backgroundImg = Image.createImage("/bg1.png");
			createBackground();
			manager.append(background);	
			
			
			InputStream is = this.getClass().getResourceAsStream("/1.mid");
			Player player = Manager.createPlayer(is,"audio/midi");
			player.realize();
			player.prefetch();
			player.start();

		} catch (Exception ex) {
			System.err.println(ex);
		}

		Thread runner = new Thread(this);
		runner.start();

	}

	public void createSprites(){
		
		coupleImg = GameTools.getImage("/d_frame.png");
		coupleX = CENTER_X;
		coupleY = BASE; // CENTER_Y;
		doraemonSprite = new DoraemonSprite(coupleImg, 20, 35);
		doraemonSprite.defineReferencePixel(doraemonSprite.getWidth() / 2,
				doraemonSprite.getHeight());
		// and adds layers to it
		manager.append(doraemonSprite);
		
		
		Image doryakisImg = GameTools.getImage("/dorayaki_m.png");
		dorayakis = new Dorayakis(doryakisImg, this.maxDoraykis,doryakisImg.getWidth(),
				doryakisImg.getHeight(),this.GAME_WIDTH,
				this.GAME_HEIGHT);
		dorayakis.initDorayakis();
		
		Image fairyWaterImg = GameTools.getImage("/Fairy_Water.png");
		fairyWaters =  new FairyWaters(this, this.maxFairyWates,fairyWaterImg, fairyWaterImg.getWidth(),
				fairyWaterImg.getHeight(), this.GAME_WIDTH,
				this.GAME_HEIGHT);
		fairyWaters.initFairyWaters();
		
		Image bombImg = GameTools.getImage("/bomb1_m.png");
		bombs =  new Bombs(this, this.maxBombs,bombImg, bombImg.getWidth(),
				bombImg.getHeight(), this.GAME_WIDTH,
				this.GAME_HEIGHT);
		bombs.initBombs();
		
		Image bomb2Img = GameTools.getImage("/bomb2_m.png");
		bomb2s =  new Bomb2s(this, this.maxBomb2s,bomb2Img, bomb2Img.getWidth(),
				bomb2Img.getHeight(), this.GAME_WIDTH,
				this.GAME_HEIGHT);
		bomb2s.initBombs();
		
		Image enhancerImg = GameTools.getImage("/enhancer_m.png");
		enhancers =  new Enhancers(this, this.maxEnhancers, enhancerImg, enhancerImg.getWidth(),
				enhancerImg.getHeight(), this.GAME_WIDTH,
				this.GAME_HEIGHT);
		enhancers.initEnhancers();
		
		Image poisonImg = GameTools.getImage("/Poison_m.png");
		poisons =  new Poisons(this, this.maxPoisons, poisonImg, poisonImg.getWidth(),
				poisonImg.getHeight(), this.GAME_WIDTH,
				this.GAME_HEIGHT);
		poisons.initPoisons();
		
		Image lifeImg = GameTools.getImage("/life.png");
		lifes = new Lifes(lifeImg,this.maxLifes, this.maxFairyWates, lifeImg.getWidth(),
				lifeImg.getHeight(), this.GAME_WIDTH,
				this.GAME_HEIGHT);
		lifes.initLifes();
		
	}
	public void run() {

		// the graphics object for this canvas
		Graphics g = getGraphics();

		clock = new Clock(Constant.TIME_LIMIT);
		new Timer().schedule(clock, 0, 1000);

		while (!stop) { // infinite loop

			// first verify game state
			verifyGameState();
			
			// automatically show the sprites
			visiableState();
			
			// check user's input
			checkUserInput();

			// update screen
			updateGameScreen(getGraphics());

			// and sleep, this controls
			// how fast refresh is done
			try {
				Thread.currentThread().sleep(70);
			} catch (Exception e) {
			}

		}
		
		// show the game result
		showGameResult();
	}

	private void verifyGameState() {

		if (this.currentLife == 0) {
			this.failed = true;
			stop = true;
			return;
		}
		
		// change the game gate when successfully pass the game
		if (clock.getTimeLeft() == 0) {
			// change background and reset the background 
			currentGate++;
			if (this.currentGate > Constant.GAME_GATE){
				stop = true;
				return;
			}
			manager.remove(background);
			backgroundImg = GameTools.getImage("/bg"+currentGate+".png");
			try {
				createBackground();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		    manager.append(background);
		    
		    // reset the timer
		    clock = new Clock(Constant.TIME_LIMIT);
		    new Timer().schedule(clock, 0, 1000);
			
		    return;
		}  
	}
	
	private void visiableState(){
		rnd=new Random();
		if((rnd.nextInt() & 0x7fffffff) % 100 == 0){
			this.fairyVisiable = true;
		}
		
		if((rnd.nextInt() & 0x7fffffff) % 200 == 0){
			this.bombVisiable = true;
		}
		
		if((rnd.nextInt() & 0x7fffffff) % 300 == 0){
			this.bomb2Visiable = true;
		}
		
		if((rnd.nextInt() & 0x7fffffff) % 400 == 0){
			this.poisonVisiable = true;
		}
		
		if((rnd.nextInt() & 0x7fffffff) % 500 == 0){
			this.enhancerVisiable = true;
		}
	}

	private void checkUserInput() {

		// get the state of keys
		int keyState = getKeyStates();

		// calculate the position for x axis
		calculateCoupleX(keyState);
		calculateCoupleY(keyState);

	}

	private void updateGameScreen(Graphics g) {

		// the next two lines clear the background
		g.setColor(0xffffff);
		g.fillRect(0, 0, getWidth(), getHeight());

		buildGameScreen(getGraphics());

		// // draws the couple image according to current
		// // desired positions
		// g.drawImage(
		// coupleImg, coupleX,
		// coupleY, Graphics.HCENTER | Graphics.BOTTOM);
		//
		
		doraemonSprite.setRefPixelPosition(coupleX, coupleY);

		// doraemonSprite.paint(g);
		manager.paint(g, 0, 0);

		spritesUpdate(g);
		
		
		// this call paints off screen buffer to screen
		flushGraphics();
	}
	private void spritesUpdate(Graphics g){
		dorayakis.paint(g);
		dorayakis.checkForCollision(doraemonSprite, true);
		
		lifes.paint(g);
		// System.out.println("main    " + this.currentLife);
		lifes.setCurrentLife(this.currentLife);
		lifes.checkForCurrentLife();

		if(fairyVisiable){
			fairyWaters.paint(g);
			fairyWaters.checkForCollision(doraemonSprite, true);
		}
		
		if(bombVisiable){
			bombs.paint(g);
			bombs.checkForCollision(doraemonSprite, true);
		}
		
		if(bomb2Visiable){
			bomb2s.paint(g);
			bomb2s.checkForCollision(doraemonSprite, true);
		}
		
		if(poisonVisiable){
			poisons.paint(g);
			poisons.checkForCollision(doraemonSprite, true);
		}
		
		if(enhancerVisiable){
			enhancers.paint(g);
			enhancers.checkForCollision(doraemonSprite, true);
		}
	}

	private void calculateCoupleX(int keyState) {

		// determines which way to move and changes the
		// x coordinate accordingly
		// if((keyState & LEFT_PRESSED) != 0) {
		// coupleX =
		// Math.max(
		// GAME_ORIGIN_X + coupleImg.getWidth()/2,
		// coupleX - dx);
		// }
		// else if((keyState & RIGHT_PRESSED) != 0) {
		// coupleX =
		// Math.min(
		// GAME_ORIGIN_X + GAME_WIDTH - coupleImg.getWidth()/2,
		// coupleX + dx);;
		// }

		  if((keyState & LEFT_PRESSED) != 0) {
			    coupleX = 
			      Math.max(
					        GAME_ORIGIN_X + doraemonSprite.getWidth()/2,
					        coupleX - dx);
			    doraemonSprite.setFrame(Constant.FRAME_DORAEMON_LEFT);
			  }
			  else if((keyState & RIGHT_PRESSED) != 0) {
			    coupleX =
			      Math.min(
					        GAME_ORIGIN_X + GAME_WIDTH - doraemonSprite.getWidth()/2,
					        coupleX + dx);
			    doraemonSprite.setFrame(Constant.FRAME_DORAEMON_RIGHT);
			  }else{
		        doraemonSprite.setFrame(Constant.FRAME_DORAEMON_NORMAL);
			  }
	}

	private void buildGameScreen(Graphics g) {

		// set the drawing color to black
		g.setColor(0x000000);

		// draw the surrounding rectangle
		g.drawRect(GAME_ORIGIN_X, GAME_ORIGIN_Y, GAME_WIDTH, GAME_HEIGHT);

		// draw the base line
		g.drawLine(GAME_ORIGIN_X, BASE, GAME_ORIGIN_X + GAME_WIDTH, BASE);

		// draw the maximum line to where the couple can jump to
		g.drawLine(GAME_ORIGIN_X, BASE - MAX_HEIGHT,
				GAME_ORIGIN_X + GAME_WIDTH, BASE - MAX_HEIGHT);
		showTimeLeft(g);
		showDorayakis(g);
		showGate(g);
		// background.paint(g);
	}

	private void calculateCoupleY(int keyState) {

		// check if the couple were on the way up
		if (up) {

			// if yes, see if they have reached the current jump height
			if ((coupleY > (BASE - jumpHeight + doraemonSprite.getHeight()))) {

				// if not, continue moving them up
				coupleY -= dy;
			} else if (coupleY == (BASE - jumpHeight + doraemonSprite
					.getHeight())) {

				// if yes, start moving them down
				coupleY += dy;

				// and change the flag
				up = false;

			}

		} else {

			// the couple are on their way down, have they reached base?
			if (coupleY < BASE) {

				// no, so keep moving them down
				coupleY += dy;

			} else if (coupleY == BASE) {

				// have reached base, so calculate a new
				// jump height which is not more than MAX_HEIGHT
				int hyper = random.nextInt(MAX_HEIGHT + 1);
				// int hyper = random.nextInt();

				// but make sure that this it is atleast greater than the image
				// height
				if (hyper > doraemonSprite.getHeight())
					jumpHeight = hyper;

				// move the image up
				coupleY -= dy;

				// and reset the flag
				up = true;

			}
		}
	}

	private void createBackground() throws IOException {

		// load the image
		//backgroundImg = Image.createImage("/bg1.png");// ("/tiledlayer1.gif");

		// create the tiledlayer background
		background = new TiledLayer(1, 1, backgroundImg, 210, 250); // (5, 5,
																	// backgroundImg,
																	// 32, 32);
		// // array that specifies what image goes where
		// int[] cells = {
		// 3, 3, 3, 3, 3, // sky
		// 3, 3, 3, 3, 3, // sky
		// 1, 1, 1, 1, 1, // earth
		// 2, 2, 2, 2, 2, // sea
		// 2, 2, 2, 2, 2 // sea
		// };
		//
		// // set the background with the images
		// for (int i = 0; i < cells.length; i++) {
		// int column = i % 5;
		// int row = (i - column)/5;
		// background.setCell(column, row, cells[i]);
		// }

		background.setCell(0, 0, 1);
		// set the location of the background
		background.setPosition(GAME_ORIGIN_X, GAME_ORIGIN_Y);

	}

	// the couple image
	private void showTimeLeft(Graphics g) {

		// what does the clock say
		int timeLeft = clock.getTimeLeft();

		// if less than 6 seconds left
		// flicker time with red and black
		if (timeLeft < 6) {
			if ((timeLeft % 2) == 0)
				g.setColor(0xff0000);
			else
				g.setColor(0x000000);
		}

		// draw the time left string
		g.drawString("Time Left: " + timeLeft + " seconds", 100, 0, 0);

		// reset the color
		g.setColor(0x000000);
	}
	private void showGate(Graphics g) {

		g.drawString("Gate: " + this.currentGate, 10, 0, 0);
		g.setColor(0x000000);
	}
	
	private void showDorayakis(Graphics g) {

		// draw the dorayakis left string
		g.drawString("Your socre: " + dorayakis.getDorayakiHits(), 10, 270, 0);

		// reset the color
		g.setColor(0x000000);
	}

	// at the end of the game show the score
	private void showGameScore(Graphics g) {

		// create a base rectangle
		g.setColor(0xffffff);
		g.fillRect(0, CENTER_Y - 20, getWidth(), 40);

		g.setColor(0x000000);

		// and show the score
		g.drawString("You hit " + dorayakis.getDorayakiHits(),
		// spriteController.getScore() + " dorayakis.",
				CENTER_X, CENTER_Y, Graphics.HCENTER | Graphics.BASELINE);

		flushGraphics();
	}

	private void showGameResult() {
		mark.setMark(dorayakis.getDorayakiHits());// (spriteController.getScore());
		// System.out.print("mark...."+mark.getMark());
		if (!failed) {
			suc = new SucForm(this.sm);
			suc.setMark(mark);
			Display.getDisplay(sm).setCurrent(suc.getMsgForm());
		} else {
			ff = new FailForm(this.sm);
			ff.setMark(mark);
			Display.getDisplay(sm).setCurrent(ff.getMsgForm());
		}
	}

	public LayerManager getManager() {
		return this.manager;
	}

	public Random getRandom() {
		return this.random;
	}

	public Sprite getDoraemonSprite() {
		return this.doraemonSprite;
	}

	public int getDx() {
		return this.dx;
	}

	public void setDx(int dx) {
		this.dx = dx;
	}
	
	public Mark getMark() {
		return mark;
	}

	public void setMark(Mark mark) {
		this.mark = mark;
	}
	
	public int getCurrentLife(){
		return this.currentLife;
	}
	
	public void setCurrentLife(int currentLife){
		this.currentLife = currentLife;
	}
	private Image coupleImg;

	// the couple image coordinates
	private int coupleX;
	private int coupleY;

	// the distance to move in the x axis
	private int dx = 1;

	// the distance to move in the y axis
	private int dy = 1;

	// the center of the screen
	public final int CENTER_X = getWidth() / 2;
	public final int CENTER_Y = getHeight() / 2;
	// the game boundary
	public static final int GAME_WIDTH = 210;
	public static final int GAME_HEIGHT = 250;

	// the shifted x,y origin of the game
	public final int GAME_ORIGIN_X = 10; // (getWidth() - GAME_WIDTH)/2;
	public final int GAME_ORIGIN_Y = 20; // (getHeight() - GAME_HEIGHT)/2;

	// the height of sections below and above the couple
	public final int SECTION_HEIGHT = 0; // 64;

	// the base on which the couple will move
	public final int BASE = GAME_ORIGIN_Y + GAME_HEIGHT - SECTION_HEIGHT;

	// the max height the couples can jump
	public final int MAX_HEIGHT = 32;

	// a flag to indicate which direction the couple are moving
	private boolean up = true;

	// indicates the random jump height, calculated for every jump
	private int jumpHeight = MAX_HEIGHT;

	// random number generator
	public Random random = new Random();

	private Image backgroundImg;
	private TiledLayer background;

	private DoraemonSprite doraemonSprite;

	private LayerManager manager;
	private Clock clock;
	private boolean stop = false;

	// private SpriteController spriteController;
	private Dorayakis dorayakis;
	private boolean failed = false;

	private SucForm suc;

	private FailForm ff;
	private StartMenu sm;


	private FairyWaters fairyWaters;
	private Bombs bombs;
	private Bomb2s bomb2s;
	private Poisons poisons;
	private Enhancers enhancers;
	private Lifes lifes;
	
	
	private int currentLife;
	
	private Random rnd;
	private boolean fairyVisiable = false;
	private boolean bombVisiable = false;
	private boolean bomb2Visiable = false;
	private boolean enhancerVisiable = false;
	private boolean poisonVisiable = false;
	

	private int currentGate = 1;
	
	private int maxDoraykis;
	private int maxFairyWates;
	private int maxBombs;
	private int maxBomb2s;
	private int maxPoisons;
	private int maxEnhancers;
	private int maxLifes;
	
	Mark mark = new Mark();
}
